// CSE571 - player-svc-handler.cpp
// Authors: Jonathan Wald, Daniel Sarfati, Guangle Fan
// Player service handler which is used for opening a connection and then (with
// R/W masks) for managing each socket connection reactively.  

#include <iostream>
#include <sstream>

#include "ace/Reactor.h"
#include "ace/Svc_Handler.h"
#include "ace/Acceptor.h"
#include "ace/Synch.h"
#include "ace/SOCK_Acceptor.h"

#include "globals.h"
#include "card.h"
#include "game.h"
#include "game-manager.h"
#include "player.h"
#include "player-game.h"
#include "player-svc-handler.h"
#include "poker.h"
#include "score-cal.h"
#include "io-manager.h"
#include "msg-flag.h"
#include "signal-handler.h"

const int TIMEOUT_VALUE = 2, TIMEOUT_VALUE2 = 2;
const int CARD_BUFFER_SIZE = 2;
const unsigned int NUM_CARDS = 5;
const unsigned int SIZE_CARD_POOL = 7;
extern const bool DEBUG;

PlayerSvcHandler::PlayerSvcHandler() {
  sent_act = false;
}

int PlayerSvcHandler::open(void*){
  MsgFlag::icrNum();
  GameManager* manager = GameManager::instance();
  // manager->games.push_back(game);
  int player_name_length = manager->get_name().length();
  ACE_Message_Block *mb = new ACE_Message_Block(player_name_length + 1);
   
  mb->copy(manager->get_name().c_str(), player_name_length + 1);
  ACE_Time_Value tv(TIMEOUT_VALUE);
  game->handler.putq(mb, &tv);
  ACE_Reactor::instance()->register_handler(this,
  	   				    ACE_Event_Handler::READ_MASK |
					    ACE_Event_Handler::WRITE_MASK);
  return 0;
}


int PlayerSvcHandler::handle_input(ACE_HANDLE){
  if (DEBUG)  std::cout << "Handling input. "<< std::endl;
  IOManager::instance()->enqueue(this, 0);
  ACE_Reactor::instance ()->mask_ops(this,
  		   		     ACE_Event_Handler::READ_MASK,
  		   		     ACE_Reactor::CLR_MASK);
  return 0;
}

int PlayerSvcHandler::handle_completed_read(string data){
  GameManager *manager = GameManager::instance();
  if (data ==  MISS_DEAL_MSG) {
    game->finalHand = false;
    game->hand.emptyHand();
    game->cardPool.emptyDeck();
    return 0;
  } else if (data ==  SERVER_QUIT_MSG) {
    counterpart_quit = true;
    manager->game_quit(game);
    game->send_msg (REMOVE_HANDLER);
    return 0;
  }

  //put add_mask behind msg_handling to prevent segment fault due to handling input after removing handler
  ACE_Reactor::instance ()->mask_ops(this,
  		   		     ACE_Event_Handler::READ_MASK,
  		   		     ACE_Reactor::ADD_MASK);

  if (game->dealer_name.empty()) {
      
    std::cout << "Connected to dealer: " << data << std::endl;
    game->dealer_name = data;

    enqueue_string_for_output(game->game_name);
    enqueue_string_for_output(game->game_type);

  } else {
    // If we get nothing back, the game isn't supported.
    if (data.empty()) {
      std::cout << "Attempted to connect to game, either "
		<< game->game_name << " but its server does not support"
		<< " game type " << game->game_type << "." << std::endl;
      counterpart_quit = true;
      manager->game_quit(game);
      ACE_Reactor::instance ()->mask_ops(this,
					 ACE_Event_Handler::READ_MASK |
					 ACE_Event_Handler::WRITE_MASK,
					 ACE_Reactor::CLR_MASK);
      game->send_msg (REMOVE_HANDLER);
       return 0; 
   }

    // We are reading cards in from dealer.
    char s = data[0];
    char r = data[1];
    Card card;
    card.set_values(s, r);
     

    // Add card to our buffer for output if its one that will contribute
    // to our final hand. 
    if (game->finalHand) {
      game->stream << card << " ";
    }

    if (game->game_type == "holdem") {
      //here we're receiving cards for card pool. Dealer sends talbe cards first, then hello cards.
      game->cardPool.add_card(card);
    }
    else {
      //we're receiving cards for hand in stud or draw.
      game->hand.add_card(card);
    }

    if (game->game_type == "holdem" && game->cardPool.size() == SIZE_CARD_POOL) {
      //we're getting hand from card pool.
      game->getPoolCard();
      game->sendBackFinal();
      finish_game();
    }
    else if (game->hand.size() == NUM_CARDS) {
      // Here, we may be getting the last card of our initial hand or
      // the last card of our discard returns.
      if (game->game_type == "stud" || game->finalHand) {
        finish_game();

      } else {
        // If we are here, then we are playing draw and need to see if we want
        // to discard.
	std::cout << "Initial hand for game " << game->game_name << ":"
		  << game->hand << std::endl;
        game->finalHand = true;
        game->discard();
        if (game->hand.size() == NUM_CARDS) {
          // Our hand is so good we aren't going to discard anything.
          // Call this method again which will cause it to compute the score.
          finish_game();
	} else {
	  game->stream << std::endl << "Receiving cards: ";
	}
      }
    }
  }
  return 0;
}

int PlayerSvcHandler::handle_output(ACE_HANDLE){
  if (DEBUG)  std::cout << "Handling out " << std::endl;
  IOManager::instance()->enqueue(this, 1);
  ACE_Reactor::instance ()->mask_ops(this,
  	   			     ACE_Event_Handler::WRITE_MASK,
  	   			     ACE_Reactor::CLR_MASK);
  return 0;
}

int PlayerSvcHandler::handle_close (ACE_HANDLE, ACE_Reactor_Mask) {
  shutdown();
  return 0;
}

void PlayerSvcHandler::finish_game() {
  string discard_string = game->stream.str();
  if (!discard_string.empty()) {
    std::cout << discard_string << std::endl;
    game->stream.str("");
  }
  // print out our buffer.
  std::cout << "Final hand for game " << game->game_name <<
    ": " << game->hand << std::endl;
  int score = getHandScore (game->hand);
  stringstream stream;
  stream << score;
  enqueue_string_for_output(stream.str());
  std::cout << "SCORE: " << score << std::endl;
  // No longer record that we sent a score and consider shutdown, instead
  // just reconnect to the game.
  if (!game->terminated) {
    game->reset();
  } else {
    game->send_msg(CLIENT_QUIT_MSG);
    game->send_msg (REMOVE_HANDLER);
  }
}

bool PlayerSvcHandler::delete_if_necessary() {
  if (game->terminated && game->score != -1) {

    game->send_msg (REMOVE_HANDLER);
    //    delete this;
    return true;
  }
  return false;
}

bool PlayerSvcHandler::suspended(ACE_Message_Block* mb) {
  if (!game->suspended) {
    return false;
  }
  game->suspension_queue.enqueue(mb);
  return true;
}

int PlayerSvcHandler::suspend (void) {
  if (game->suspended) {
    std::cout << game->game_name << " is already suspended." << std::endl;
    return -1;
  }

  game->suspended = true;
  game->mask = ACE_Reactor::instance()->mask_ops(this, game->mask,
  					 ACE_Reactor::GET_MASK);
  ACE_Reactor::instance()->mask_ops(this, ACE_Event_Handler::READ_MASK |
  				    ACE_Event_Handler::WRITE_MASK,
  					 ACE_Reactor::CLR_MASK);
  first_resume = NULL;
  suspend_block = (ACE_Message_Block*)-1;
  return 0;
}

int PlayerSvcHandler::resume (void) {
  if (!game->suspended) {
    return -1;
  }
  game->suspended = false;
  ACE_Reactor::instance()->mask_ops(this, game->mask,
  				    ACE_Reactor::SET_MASK);
  ACE_Message_Block* mb;
  
  while(game->suspension_queue.dequeue(mb) != -1) {
    if(first_resume == NULL) first_resume = mb;
    IOManager::instance()->putq(mb);
  }
  resume_block = (ACE_Message_Block*)-1;
  return 0;
}

bool PlayerSvcHandler::last_act() {
  GameManager* manager = GameManager::instance(); 
  for(unsigned int i = 0; i< manager->games.size(); ++i) {
    if(manager->games[i]->handler.sent_act == false) {
      return false;
    }
  }
  return true;
}
